/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package deadlocksim.backend.process;
import deadlocksim.*;
import deadlocksim.backend.*;
import deadlocksim.backend.resources.*;
import java.util.*;
/**
 *
 * @author Jason Kabel
 */
public class DeadlockProcess 
{
    private String name;
    private int id;
    private Queue<ProcessAction> actionQueue;
    private Queue<ProcessAction> workingActionQueue;
    // map of resource types and how many resources of each type the process will need in its lifetime
    private HashMap<Integer, Integer> totalResourceNeeds;
    private SimClock clock;        
    private ResourceManager resourceManager;
    private DeadlockSim mySim;
    
    private boolean printActivity;
    private boolean isDeadlocked;
    private boolean isHalted;
                
    public DeadlockProcess()
    {
        this(null, null, 0);
    }
    public DeadlockProcess(Queue<ProcessAction> newActionQueue, DeadlockSim mySim, int pID)
    {
        
        id = pID;
        actionQueue = newActionQueue;
        totalResourceNeeds = buildResourceNeeds(newActionQueue);
        // Create a copy to work with
        workingActionQueue = new LinkedList<ProcessAction>(actionQueue);
        this.mySim = mySim;
        setClock(mySim.getSimClock());
        setResourceManager(mySim.getResourceManager());
        printsActivity(true);
        setDeadlocked(false);
        isHalted = false;
    }
    
    public void addAction(ProcessAction a)
    {
        actionQueue.add(a);
    }
    
    public void interruptWithAction(ProcessAction a)
    {
        if(workingActionQueue instanceof LinkedList)
        {
            LinkedList queueAsList = (LinkedList)workingActionQueue;
            queueAsList.addFirst(a);
        }
    }
    
    private HashMap<Integer, Integer> buildResourceNeeds(Queue<ProcessAction> newActionQueue)
    {
        HashMap<Integer, Integer> newResourceNeeds = new HashMap<Integer, Integer>();
        Iterator<ProcessAction> actionIterator = newActionQueue.iterator();
        ProcessAction nextAction;
        //RequestResourceAction nextRequestAction;
        Integer count;
        int resourceId;
        
        for (int i = 0; i < newActionQueue.size(); i++)
        {
            nextAction = actionIterator.next();
            if (nextAction instanceof RequestResourceAction)
            {
                resourceId = ((RequestResourceAction)nextAction).getResourceTypeID();
                count = newResourceNeeds.get(resourceId);
                if (count == null)
                    count = 0;
                else
                    count++;
                newResourceNeeds.put(resourceId, count);
            }
        }
        return newResourceNeeds;
    }
      

    public void clearActionQueue()
    {
        actionQueue.clear();
    }
    
    // Helper for bankers Algorithm
    //  Determines the most of each resource p will need at once for the rest
    //  of this execution
    public Map<Integer, Integer> countMaxResources()
    {
        Map<Integer, Integer> maxResourceMap = new HashMap<Integer, Integer>();
        Map<Integer, Integer> workingResourceMap = new HashMap<Integer, Integer>(); //used in calculation
        Iterator<ProcessAction> actionsIter = workingActionQueue.iterator();
        
        while(actionsIter.hasNext())
        {
            ProcessAction nextAction = actionsIter.next();
            if(nextAction instanceof RequestResourceAction)
            {
                RequestResourceAction rAction = (RequestResourceAction)nextAction;
                int resourceID = rAction.getResourceTypeID();
                if(workingResourceMap.containsKey(resourceID))
                {
                    int currentTotalForID = workingResourceMap.get(resourceID) + 1;
                    workingResourceMap.put(resourceID, currentTotalForID);
                    if(maxResourceMap.containsKey(resourceID))
                    {
                        int currentMaxForID = maxResourceMap.get(resourceID);
                        if(currentTotalForID > currentMaxForID)
                        {
                            // We have reached a new maximum total for this resource
                            //  store it permanently
                            maxResourceMap.put(resourceID, currentTotalForID);
                        }
                    }
                    else
                    {
                        // Initialize the entry for resourceID in the map
                        maxResourceMap.put(resourceID, currentTotalForID);
                    }
                }
                else
                {
                    workingResourceMap.put(resourceID, 1);
                    maxResourceMap.put(resourceID, 1);
                }
            }
            else if(nextAction instanceof FreeResourceAction)
            {
                
                FreeResourceAction fAction = (FreeResourceAction)nextAction;
                int resourceID = fAction.getResourceTypeID();
                // Reduce the working count of resourceID
                if(workingResourceMap.containsKey(resourceID))
                {
                    int currentNumberForID = workingResourceMap.get(resourceID) - 1;
                    workingResourceMap.put(resourceID, currentNumberForID);
                }
            }
        }
        return maxResourceMap;
    }
    
    public void executePhase()
    {
        
        doProcessAction(); // Process next action(s) from the actionQueue
        endPhase(); // Tell whoever cares that this phase is done
    }
    
    public Queue<ProcessAction> getActionQueue()
    {
        return actionQueue;
    }
    public int getID()
    {
        return this.id;
    }

    
    public int getResourceNeeds(Integer resourceType)
    {
        Integer numWanted = 0;
        numWanted = totalResourceNeeds.get(resourceType);
        if (numWanted == null)
            return 0;
        else
            return numWanted;
    }
    
    public void halt()
    {
        if(!mySim.isRunning() && !mySim.isStepping())
            return; // Do nothing
        
        setDeadlocked(false);
        this.isHalted = true;
        clock.processHalted(this);
        workingActionQueue.clear();
        resourceManager.freeAllFromProcess(this);
        resourceManager.removeResourceRequests(this);
        
        if(printActivity)
            printActivity("Halt action complete.");
    }
    
    public boolean isDeadlocked()
    {
        return isDeadlocked;
    }
    
    public boolean isHalted()
    {
        return isHalted;
    }
    public void printActivity(String actionDescription)
    {
        mySim.sendMessage("Process " + this.id + 
                            " at time " + clock.getTime() + ": " +
                            actionDescription, false);
    }
    public void printsActivity(boolean print)
    {
        printActivity = print;
    }
    
    public ProcessAction removeAction()
    {
        return actionQueue.remove();
    }
    public void reset()
    {
        //clock.deregisterProcess(this);
        resourceManager.resetProcess(this);
        resetActionQueue();
        isDeadlocked = false;
        isHalted = false;
    }
    public void resetActionQueue()
    {
        workingActionQueue.clear();
        workingActionQueue.addAll(actionQueue);
    }
    
    public void setClock(SimClock aClock)
    {
        if(clock != null)
        {
            clock.deregisterProcess(this);
        }
        clock = aClock;
        clock.registerProcess(this);
    }
    
    public void setDeadlocked(boolean isDeadlocked)
    {
        this.isDeadlocked = isDeadlocked;
    }
    
    public void setResourceManager(ResourceManager rManager)
    {
        resourceManager = rManager;
    }
    
   
    
    private void doProcessAction()
    {
        if(workingActionQueue.isEmpty())
        {
            clock.processHalted(this);
            return; // Nothing to do
        }
        
        ProcessAction currAction = workingActionQueue.peek();
        if(currAction instanceof RequestResourceAction)
        { 
            ResourceAction resourceAction = (ResourceAction)currAction;
            String resultString = "Requesting R" + resourceAction.getResourceTypeID() + "...";
            //if(printActivity)
            //    printActivity("Requesting resource with ID " + resourceAction.getResourceTypeID());
            ProcessResource requestedResource 
                    = resourceManager.requestResource(this, resourceAction.getResourceTypeID());
            if(requestedResource == null)
            {
                // Leave the "requestResourceAction" on the stack for the next phase
                // This is essentially the same as a "wait" for this phase.
                
                if(printActivity)
                {
                    if(isDeadlocked())
                    {
                        printActivity(resultString + "I'm DEADLOCKED!");
                    }
                    else
                        printActivity(resultString + "still waiting...");
                }
                return;
            }
            else
            {
                // We don't need to manage the requested resource; ResourceManager
                //  will track our "in-use" resources for us.
                
                // We get our resource; since this is a "zero-time" action,
                //  process the next action too;
                workingActionQueue.remove(); // Remove the request action; we're doing it now.
                if(mySim.zeroTimeResourceActions())
                    doProcessAction();
                if(printActivity)
                    printActivity(resultString + "Request successful!");
                return;
            }
        }
        else if(currAction instanceof HaltAction)
        {
            halt();
            return;
        }
        else if(currAction instanceof ComputeAction)
        {
            workingActionQueue.remove(); // We're executing this computation
            if(printActivity)
                    printActivity("Computing...");
            // See if we should free our resource
            ProcessAction nextAction = workingActionQueue.peek();
            if (nextAction instanceof FreeResourceAction)
            {
                // do the next action too.
                if(mySim.zeroTimeResourceActions())
                    doProcessAction();
                return;
            }
        }
        else if(currAction instanceof FreeResourceAction)
        {
            if(printActivity)
                    printActivity("Freeing resource of ID ..." 
                            + ((ResourceAction)currAction).getResourceTypeID());
            workingActionQueue.remove(); // Use up this free resource action.
            resourceManager.freeResource(this, ((ResourceAction)currAction).getResourceTypeID());
            return;
        }
    }
    
    private void endPhase()
    {
        clock.processPhaseComplete(this);
    }
}
